﻿using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

using Flagwind.Plugins;

namespace Flagwind.Web.Plugins
{
	public class PluginControllerFactory : IControllerFactory
	{
		#region 常量定义

		private const string ROOT_CONTROLLERS_PATH = "/Workspace/Controllers";

		#endregion

		#region 私有变量

		private PluginContext _pluginContext;

		#endregion

		#region 构造函数

		public PluginControllerFactory(PluginContext pluginContext)
		{
			if(pluginContext == null)
				throw new ArgumentNullException("pluginContext");

			_pluginContext = pluginContext;
		}

		#endregion

		#region 接口成员

		public IController CreateController(RequestContext requestContext, string controllerName)
		{
			var node = this.GetControllerNode(requestContext, controllerName);

			if(node == null || node.NodeType == PluginTreeNodeType.Empty)
				throw new HttpException(404, "Not found." + Environment.NewLine + requestContext.HttpContext.Request.RawUrl);

			var value = node.UnwrapValue(ObtainMode.Alway, this);

			if(value != null && typeof(Delegate).IsAssignableFrom(value.GetType()))
				value = ((Delegate)value).DynamicInvoke();

			var controller = value as Controller;

			if(controller != null)
				controller.ActionInvoker = new PluginControllerActionInvoker();

			return controller;
		}

		public void ReleaseController(IController controller)
		{
			IDisposable disposable = controller as IDisposable;

			if(disposable != null)
				disposable.Dispose();
		}

		System.Web.SessionState.SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
		{
			var node = this.GetControllerNode(requestContext, controllerName);

			if(node == null || node.NodeType == PluginTreeNodeType.Empty)
				return System.Web.SessionState.SessionStateBehavior.Default;

			Type controllerType = null;

			switch(node.NodeType)
			{
				case PluginTreeNodeType.Builtin:
					controllerType = ((Builtin)node.Value).BuiltinType.Type;
					break;
				case PluginTreeNodeType.Custom:
					controllerType = node.Value == null ? null : node.Value.GetType();
					break;
			}

			if(controllerType == null)
				return System.Web.SessionState.SessionStateBehavior.Default;

			var attribute = controllerType.GetCustomAttributes(typeof(SessionStateAttribute), inherit: true)
									.OfType<SessionStateAttribute>()
									.FirstOrDefault();

			return (attribute != null) ? attribute.Behavior : System.Web.SessionState.SessionStateBehavior.Default;
		}

		#endregion

		#region 私有方法

		private PluginTreeNode GetControllerNode(RequestContext requestContext, string controllerName)
		{
			Route route = requestContext.RouteData.Route as Route;

			if(route == null)
				return null;

			var controllerPath = string.Empty;
			var area = VirtualPathUtility.GetArea(requestContext.RouteData);

			if(string.IsNullOrWhiteSpace(area))
				controllerPath = PluginPath.Combine(ROOT_CONTROLLERS_PATH, controllerName);
			else
				controllerPath = PluginPath.Combine(ROOT_CONTROLLERS_PATH, area, controllerName);

			requestContext.RouteData.DataTokens["area"] = area;
			requestContext.RouteData.DataTokens["controller.path"] = controllerPath;

			return _pluginContext.PluginTree.Find(controllerPath);
		}

		#endregion
	}
}
